package Code.Objects.Characters.Player.GUI;

import org.jrabbit.base.core.types.Renderable;
import org.jrabbit.base.core.types.Updateable;
import org.jrabbit.base.data.structures.UpdateList;
import org.jrabbit.base.graphics.primitive.PrimitivePainter;
import org.jrabbit.base.graphics.transforms.Color;
import org.jrabbit.base.graphics.transforms.Vector2d;
import org.jrabbit.base.managers.ResourceManager;
import org.jrabbit.standard.game.objects.Sprite;

import Code.Basic.Entity.GenericCharacter;

public class ObjectiveFinder extends Sprite implements Updateable
{
	private boolean active;
	private double minRange;
	private double maxRange;
	private UpdateList<TargetFinder> targetFinders;
	private double distanceFromOwner;

	public ObjectiveFinder()
	{
		super("Resources/Images/GUI/Game/Objective/Objective Pointer.png");
		active = true;
		minRange = 100;
		maxRange = 250;
		scalar.setScaleX(1.5f);
		targetFinders = new UpdateList<TargetFinder>();
		distanceFromOwner = 50;
	}

	public void clearTargets()
	{
		targetFinders.clear();
	}

	public void addTarget(Vector2d loc)
	{
		targetFinders.add(new PointFinder(loc));
	}

	public void addTarget(GenericCharacter target)
	{
		targetFinders.add(new CharacterFinder(target));
	}

	public void addTarget(Vector2d[] path)
	{
		targetFinders.add(new PathFinder(path));
	}

	public void removeTarget(Vector2d point)
	{
		TargetFinder toRemove = null;
		for(TargetFinder tF:targetFinders)
		{
			if(tF instanceof PointFinder)
			{
				if(((PointFinder) tF).getTargetLocation().equals(point))
				{
					toRemove = tF;
				}
			}
		}
		targetFinders.release();
		if(toRemove != null)
		{
			targetFinders.remove(toRemove);
		}
	}

	public void removeTarget(GenericCharacter target)
	{
		TargetFinder toRemove = null;
		for(TargetFinder tF:targetFinders)
		{
			if(tF instanceof CharacterFinder)
			{
				if(((CharacterFinder) tF).getTarget().equals(target))
				{
					toRemove = tF;
				}
			}
		}
		targetFinders.release();
		if(toRemove != null)
		{
			targetFinders.remove(toRemove);
		}
	}

	public void setTarget(Vector2d loc)
	{
		clearTargets();
		addTarget(loc);
	}

	public void setTarget(GenericCharacter target)
	{
		clearTargets();
		addTarget(target);
	}

	public void setTarget(Vector2d[] path)
	{
		clearTargets();
		addTarget(path);
	}

	public void setActive(boolean b)
	{
		active = b;
	}

	public boolean isActive()
	{
		return active;
	}

	public void render(Vector2d loc)
	{
		if(active)
		{
			for(TargetFinder targetFinder:targetFinders)
			{
				Vector2d target = targetFinder.getTargetLocation();
				if(target != null)
				{
					color.set(targetFinder.color());
					location.set(loc);
					Vector2d diff = target.difference(location);
					double distance = diff.magnitude();
					if(distance > minRange)
					{
						if(distance < maxRange)
						{
							color.setAlpha((float) ((distance - minRange) / (maxRange - minRange)));
						}
						else
						{
							color.setAlpha(1f);
						}
						double angle = diff.direction();
						rotation.set(angle);
						location.add(diff.unitVector(), distanceFromOwner);
						render();
					}
					targetFinder.render();
				}
			}
			targetFinders.release();
		}
	}
	public void update(int delta)
	{
		for(TargetFinder tF:targetFinders)
		{
			tF.update(delta);
		}
		targetFinders.release();
	}

	private abstract class TargetFinder implements Renderable, Updateable
	{
		protected TargetMarker marker;

		public TargetFinder()
		{
			marker = new TargetMarker();
		}

		public Color color()
		{
			return marker.color();
		}

		public void render()
		{
			if(marker.onscreen())
			{
				marker.render();
			}
		}

		public void update(int delta)
		{
			marker.update(delta);
		}

		public abstract Vector2d getTargetLocation();
	}

	private class PointFinder extends TargetFinder
	{
		private Vector2d targetLoc;

		public PointFinder(Vector2d loc)
		{
			targetLoc = loc;
			marker.location().set(targetLoc);
			marker.color().set(1f, 1f, 0);
		}

		public Vector2d getTargetLocation()
		{
			return targetLoc;
		}
	}

	private class CharacterFinder extends TargetFinder
	{
		private GenericCharacter target;

		public CharacterFinder(GenericCharacter c)
		{
			target = c;
			marker.color().set(1f, 0, 0);
		}

		public GenericCharacter getTarget()
		{
			return target;
		}

		public Vector2d getTargetLocation()
		{
			if(target != null)
			{
				if(!target.isDead())
				{
					return target.location();
				}
			}
			return null;
		}

		public void render()
		{
			Vector2d targetLocation = getTargetLocation();
			if(targetLocation != null)
			{
				marker.location().set(getTargetLocation());
				if(marker.onscreen())
				{
					marker.render();
				}
			}
		}
	}

	private class PathFinder extends TargetFinder
	{
		private Color passedColor = new Color(0.75f, 0.5f, 0);
		private Color firstColor = new Color(0, 0.3f, 1f);
		private Color midColor = new Color(0.3f, 1f, 0);
		private Color lastColor = new Color(1f, 1f, 0);

		private Vector2d[] waypoints;
		private int selectedPoint;

		private double waypointRadius;

		public PathFinder(Vector2d[] points)
		{
			waypoints = points;
			selectedPoint = 0;
			waypointRadius = 100;
		}

		public Color color()
		{
			if(selectedPoint < waypoints.length - 1)
			{
				return firstColor;
			}
			return lastColor;
		}

		public void render()
		{
			PrimitivePainter.setLineWidth(2);
			Vector2d lastPoint = null;
			Color colorA = firstColor;
			Color colorB = lastColor;

			for(int i = 0; i < waypoints.length; i++)
			{
				if(i == waypoints.length - 1)
				{
					colorB = lastColor;
					if(lastPoint != null)
					{
						PrimitivePainter.drawLine(lastPoint, waypoints[i], colorA, colorB);
					}
					colorA = lastColor;
					marker.scalar().set(1.5f);
				}
				else if(i == selectedPoint)
				{
					colorB = firstColor;
					if(lastPoint != null)
					{
						PrimitivePainter.drawLine(lastPoint, waypoints[i], colorA, colorB);
					}
					colorA = firstColor;
					marker.scalar().set(1);
				}
				else if(i > selectedPoint)
				{
					colorB = midColor;
					if(lastPoint != null)
					{
						PrimitivePainter.drawLine(lastPoint, waypoints[i], colorA, colorB);
					}
					colorA = midColor;
					marker.scalar().set(0.5f);
				}
				else
				{
					colorB = passedColor;
					if(lastPoint != null)
					{
						PrimitivePainter.drawLine(lastPoint, waypoints[i], colorA, colorB);
					}
					colorA = passedColor;
					marker.scalar().set(0.25f);
				}
				marker.location().set(waypoints[i]);
				if(marker.onscreen())
				{
					marker.color().set(colorA);
					marker.render();
				}
				lastPoint = waypoints[i];
			}
		}

		public Vector2d getTargetLocation()
		{
			check(location());
			return waypoints[selectedPoint];
		}

		private void check(Vector2d charPoint)
		{
			if(selectedPoint < waypoints.length - 1)
			{
				if(waypoints[selectedPoint].distanceTo(charPoint) < waypointRadius)
				{
					selectedPoint++;
				}
				else
				{
					double distance = waypoints[selectedPoint].distanceTo(charPoint);
					for(int i = selectedPoint + 1; i < waypoints.length; i++)
					{
						double newDist = waypoints[i].distanceTo(charPoint);
						if(newDist < distance)
						{
							distance = newDist;
							selectedPoint = i;
						}
					}
				}
			}
		}
	}

	private class TargetMarker extends Sprite implements Updateable
	{
		public TargetMarker()
		{
			super("Resources/Images/GUI/Game/Objective/Objective Target.png");
			rotation.set(ResourceManager.random().nextDouble() * 360);
		}

		public void update(int delta)
		{
			rotation.rotate(delta * 0.0075);
		}
	}
}